package com.yang.auth.runner;

import com.yang.auth.constant.RedisKeyConstant;
import com.yang.auth.dao.PermissionDao;
import com.yang.auth.dao.RoleDao;
import com.yang.auth.dao.RolePermissionRelDao;
import com.yang.auth.model.entity.Permission;
import com.yang.auth.model.entity.Role;
import com.yang.auth.model.entity.RolePermissionRel;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 在spring启动之后加载角色对应的权限
 */
@Slf4j
@Component
public class RolePermissionLoaderRunner implements ApplicationRunner {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private RoleDao roleDao;
    @Resource
    private PermissionDao permissionDao;
    @Resource
    private RolePermissionRelDao rolePermissionRelDao;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //  避免分布式情况下重复加载角色权限，设置分布式锁
        String permissionLoaderLockKey = RedisKeyConstant.getPermissionLoaderLockKey();
        Boolean setSuccess = redisTemplate.opsForValue().setIfAbsent(permissionLoaderLockKey, 1, 30, TimeUnit.SECONDS);
        if (!setSuccess) {
            return;
        }
        log.info("==> 服务启动，开始同步角色权限数据到 Redis 中...");
        //  1.查询出所有的未被禁用角色核心字段，id、角色名、角色唯一标识
        List<Role> roles=  roleDao.selectAllEnableRoleCoreField();
        //       收集角色id集合
        Set<Long> roleIds = roles.stream().map(Role::getId).collect(Collectors.toSet());

        //  2.查询出app内所有的权限（按钮权限，即发布笔记、发布评论）
        List<Permission> permissions = permissionDao.selectAppAllEnablePermissionCoreField();
        //      将权限按照id进行映射
        Map<Long, Permission> permissionMap = permissions.stream().collect(Collectors.toMap(Permission::getId, permission -> permission));

        //   3. 查询出角色与权限的关联信息
        List<RolePermissionRel> rolePermissionRels = rolePermissionRelDao.selectAllCoreFieldByRoleIds(roleIds);
        //          3.2.对权限id按照角色ID进行分组
        Map<Long, Set<Long>> roleIdPermissionIdSetMap = rolePermissionRels.stream().collect(
                //  roleId作为key，permissionId集合作为value
                Collectors.groupingBy(RolePermissionRel::getRoleId, Collectors.mapping(RolePermissionRel::getPermissionId, Collectors.toSet()))
        );

        //  4.将角色和权限相关联
        Map<Long, Set<Permission>> roleIdPermissionSetMap = new HashMap<>();
        roleIds.forEach(roleId -> {
            //  获取角色的所有权限
            Set<Long> permissionIds = roleIdPermissionIdSetMap.get(roleId);
            //  将权限id映射为对应权限
            Set<Permission> permissionSet = permissionIds.stream()
                    .map(permissionId -> permissionMap.get(permissionId))
                    .collect(Collectors.toSet());
            //  存储角色的权限
            roleIdPermissionSetMap.put(roleId, permissionSet);
        });


        //  4.将权限同步到redis
        roleIdPermissionSetMap.entrySet().forEach(kv ->{
            //      生成权限key
            String key = RedisKeyConstant.getRolePermissionKey(kv.getKey());
            redisTemplate.delete(key);
            for (Permission permission : kv.getValue()) {
                redisTemplate.opsForSet().add(key, permission);
            }
        });

        log.info("==> 服务启动，成功同步角色权限数据到 Redis 中...");
    }
}
